////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2010.
// -------------------------------------------------------------------------
//  File name:	AssetModelItem.cpp
//  Version:	v1.00
//  Created:	15/04/2009 by Paulo Zaffari
//  Description:	Implementation of AssetModelItem.h
// -------------------------------------------------------------------------  
//  History:
//		15/04/2009	11:00 - Paulo Zaffari - created
//		15/03/2010	19:51 - Nicusor Nedelcu - refactored
//
//////////////////////////////////////////////////////////////////////////// 

#include "StdAfx.h"
#include "AssetModelItem.h"
#include "AssetModelDatabase.h"
#include "Util/PathUtil.h"
#include "Include/IAssetViewer.h"
#include "StringUtils.h"
#include "IMaterial.h"
#include "Controls/AssetViewer.h"

// uncomment to use CGF streaming in caching operations
//#define ASSET_BROWSER_USE_CGF_STREAMING

namespace AssetViewer
{
	const float	kInteractiveRenderModelTranslateMultiplier = 0.02f; 
	const float	kInteractiveRenderModelRotateMultiplier = 0.02f; 
	const float	kInteractiveRenderModelZoomMultiplier = 0.006f;
	const UINT	kModelAssetWireframeToggleKey = 'W';
	const float kModelPreviewCameraZoomFactor = 0.1f;
	XmlNodeRef s_assetThumbTimeOfDay = NULL;
};

UINT CAssetModelItem::m_frameBufferScreenshot[CAssetModelItem::kAssetDisplay_MaxThumbImageBufferSize];
CAssetBrowserPreviewModelDlg	CAssetModelItem::s_modelPreviewDlg;

CAssetModelItem::CAssetModelItem(): 
CAssetDisplay(),
m_camTarget(0,0,0),
m_camRadius(1),
m_pObject(NULL),
m_pRenderer(gEnv->pRenderer),
m_bGrid(false),
m_bAxis(true),
m_clearColor( 0.47f, 0.63f, 0.8f ),
m_bRotate(false),
m_rotateAngle(0),
m_bShowObject(true),
m_fov(60),
m_rotationX(AssetBrowser::kDefaultModelRotationAngleX),
m_rotationY(AssetBrowser::kDefaultModelRotationAngleY),
m_translateX(0),
m_translateY(0),
m_camZoom(1),
m_lodCount(0),
m_triangleCount(0), 
m_vertexCount(0), 
m_submeshCount(0),
m_textureSize(0), 
m_physicsTriCount(0), 
m_physicsSize(0),
m_bSplitLODs(false),
m_bWireframe(false),
m_bIsLod(false),
m_dragCreationMode(eAssetModelItemDragCreationMode_AsBrush)
{
	m_flags |=	eAssetFlags_ThreadFieldsInfoCachingSupported | eAssetFlags_InteractiveRenderSupported |
							eAssetFlags_CanBeDraggedInViewports | eAssetFlags_CanBeMovedAfterDroppedIntoViewport;
	m_aabb.Reset();
	m_crtRotation.CreateIdentity();
	memset( m_triCountLOD, 0, sizeof( m_triCountLOD ) );
	memset( m_vertCountLOD, 0, sizeof( m_vertCountLOD ) );
	memset( m_subMeshCountLOD, 0, sizeof( m_subMeshCountLOD ) );
	memset( m_meshSizeLOD, 0, sizeof( m_meshSizeLOD ) );

	CDLight light;

	light.SetPosition( Vec3( 1, 1, -1 ) );
	light.SetLightColor( ColorF( 1, 1, 1, 1 ) );
	light.m_fRadius = 1000;
	light.m_Flags |= DLF_DIRECTIONAL;
	m_lights.push_back( light );

	if (NULL == AssetViewer::s_assetThumbTimeOfDay)
	{
		AssetViewer::s_assetThumbTimeOfDay = GetISystem()->LoadXmlFromFile("Editor/asset_thumbnail.tod");
	}
}

CAssetModelItem::~CAssetModelItem()
{
	// empty, call FreeData first
}

HRESULT STDMETHODCALLTYPE CAssetModelItem::QueryInterface( const IID &riid, void **ppvObj ) 
{ 
	if( riid == __uuidof(IAssetDisplay)/* && m_pIntegrator*/ )
	{
		*ppvObj = this;
		return S_OK;
	}
	
	return E_NOINTERFACE; 
}

ULONG STDMETHODCALLTYPE CAssetModelItem::AddRef()
{
	return ++m_ref; 
}

ULONG STDMETHODCALLTYPE CAssetModelItem::Release() 
{ 
	if( (--m_ref) == 0 )
	{
		FreeData();
		delete this;
		return 0; 
	}
	else
		return m_ref;
}

bool CAssetModelItem::Cache()
{
	if( m_flags & eAssetFlags_Cached )
		return true;

#ifdef ASSET_BROWSER_USE_CGF_STREAMING
	if( m_pObject )
	{
		if( m_pObject->m_eStreamingStatus == EFileStreamingStatus::ecss_Ready )
		{
			SetFlag( IAssetDisplay::eAssetFlags_Cached, true );
			m_pObject->Refresh( FRO_GEOMETRY );
			return true;
		}
	
		return false;
	}
#endif

	if( !m_pObject )
	{

#ifdef ASSET_BROWSER_USE_CGF_STREAMING
		m_pObject = gEnv->p3DEngine->LoadStatObj( m_strRelativePath + m_strFilename, 0, 0, true );
#else
		m_pObject = gEnv->p3DEngine->LoadStatObj( m_strRelativePath + m_strFilename, 0, 0, false );
#endif

		if( !m_pObject )
		{
			return false;
		}

		m_pObject->AddRef();

#ifdef ASSET_BROWSER_USE_CGF_STREAMING
		if( m_pObject->m_eStreamingStatus == EFileStreamingStatus::ecss_NotLoaded )
		{
			m_pObject->StartStreaming( false, NULL );
		}
#endif
		m_aabb.min = m_pObject->GetBoxMin();
		m_aabb.max = m_pObject->GetBoxMax();

		IStatObj::SStatistics stats;

		stats.pTextureSizer = gEnv->pSystem->CreateSizer();
		m_pObject->GetStatistics( stats );

		if( !stats.nIndices || !stats.nVertices )
		{
			SAFE_RELEASE( stats.pTextureSizer );
			SAFE_RELEASE( m_pObject );
			return false;
		}

		m_lodCount = stats.nLods;
		m_triangleCount = stats.nIndices / 3;
		m_vertexCount = stats.nVertices;
		m_submeshCount = stats.nSubMeshCount;
		m_nRef = stats.nNumRefs;
		m_physicsTriCount = stats.nPhysPrimitives;
		m_physicsSize = (stats.nPhysProxySize + 512) / 1024;	// In KBs
		m_bSplitLODs = stats.bSplitLods;
		m_textureSize = stats.pTextureSizer->GetTotalSize() / 1024;	// In KBs
		
		for( int i = 0; i < MAX_STATOBJ_LODS_NUM; ++i )
		{
			IStatObj* pLodObj = m_pObject->GetLodObject( i );
			IStatObj::SStatistics lodStats;

			m_triCountLOD[i] = stats.nIndicesPerLod[i] / 3;

			if( pLodObj )
			{
				pLodObj->GetStatistics( lodStats );
				m_vertCountLOD[i] = lodStats.nVertices;
				m_subMeshCountLOD[i] = lodStats.nSubMeshCount;
				m_meshSizeLOD[i] = lodStats.nMeshSize;
			}
		}

		SAFE_RELEASE( stats.pTextureSizer );

		size_t subMtls = m_pObject->GetMaterial()->GetSubMtlCount();
		IMaterial* pMtl = NULL;
		
		m_dependencies.clear();

		if( !subMtls )
		{
			pMtl = m_pObject->GetMaterial();

			// fill up dependencies
			if( pMtl )
			{
				IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

				pMtl->RequestTexturesLoading( 0 );

				m_dependencies["Materials"].insert( pMtl->GetName() );

				if( pShaderRes )
				{
					for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
					{
						m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
					}
				}
			}
		}
		else
		for( size_t s = 0; s < subMtls; ++s )
		{
			IMaterial* pMtl = m_pObject->GetMaterial()->GetSubMtl( s );

			// fill up dependencies
			if( pMtl )
			{
				IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

				pMtl->RequestTexturesLoading( 0 );

				m_dependencies["Materials"].insert( pMtl->GetName() );

				if( pShaderRes )
				{
					for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
					{
						m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
					}
				}
			}
		}
	}
	
	PrepareThumbTextInfo();
	
	if( !(m_flags & eAssetFlags_CachedFieldsInfo) )
	{
		SetFlag( IAssetDisplay::eAssetFlags_CachedFieldsInfo, true );
		GetOwnerDisplayDatabase()->OnMetaDataChange(this);
	}

#ifdef ASSET_BROWSER_USE_CGF_STREAMING
	return false; // we did not fully cached yet
#else
	SetFlag( IAssetDisplay::eAssetFlags_Cached, true );
	return true;
#endif
}

bool CAssetModelItem::CacheFieldsInfo()
{
	if( m_flags & eAssetFlags_CachedFieldsInfo )
	{
		return true;
	}

	IStatObj* pObject = gEnv->p3DEngine->LoadStatObj( m_strRelativePath + m_strFilename, 0, 0, false, IStatObj::ELoadingFlagsPreviewMode );

	if( !pObject )
	{
		return false;
	}

	pObject->AddRef();
	m_aabb.min = pObject->GetBoxMin();
	m_aabb.max = pObject->GetBoxMax();

	IStatObj::SStatistics stats;

	stats.pTextureSizer = gEnv->pSystem->CreateSizer();
	pObject->GetStatistics( stats );

	if( !stats.nIndices || !stats.nVertices )
	{
		stats.pTextureSizer->Release();
		SAFE_RELEASE(pObject);
		return false;
	}

	m_lodCount = stats.nLods;
	m_triangleCount = stats.nIndices / 3;
	m_vertexCount = stats.nVertices;
	m_submeshCount = stats.nSubMeshCount;
	m_nRef = stats.nNumRefs;
	m_physicsTriCount = stats.nPhysPrimitives;
	m_physicsSize = (stats.nPhysProxySize + 512) / 1024;	// In KBs
	m_bSplitLODs = stats.bSplitLods;
	m_textureSize = stats.pTextureSizer->GetTotalSize() / 1024;	// In KBs

	for( int i = 0; i < MAX_STATOBJ_LODS_NUM; ++i )
	{
		IStatObj* pLodObj = pObject->GetLodObject( i );
		IStatObj::SStatistics lodStats;

		m_triCountLOD[i] = stats.nIndicesPerLod[i] / 3;

		if( pLodObj )
		{
			pLodObj->GetStatistics( lodStats );
			m_vertCountLOD[i] = lodStats.nVertices;
			m_subMeshCountLOD[i] = lodStats.nSubMeshCount;
			m_meshSizeLOD[i] = lodStats.nMeshSize;
		}
	}

	SAFE_RELEASE( stats.pTextureSizer );

	if( pObject->GetMaterial() )
	{
		size_t subMtls = pObject->GetMaterial()->GetSubMtlCount();
		IMaterial* pMtl = NULL;

		m_dependencies.clear();

		if( !subMtls )
		{
			pMtl = pObject->GetMaterial();

			// fill up dependencies
			if( pMtl )
			{
				IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

				pMtl->RequestTexturesLoading( 0 );
				m_dependencies["Materials"].insert( pMtl->GetName() );

				if( pShaderRes )
				{
					for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
					{
						m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
					}
				}
			}
		}
		else
		for( size_t s = 0; s < subMtls; ++s )
		{
			IMaterial* pMtl = pObject->GetMaterial()->GetSubMtl( s );

			// fill up dependencies
			if( pMtl )
			{
				IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

				pMtl->RequestTexturesLoading( 0 );
				m_dependencies["Materials"].insert( pMtl->GetName() );

				if( pShaderRes )
				{
					for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
					{
						m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
					}
				}
			}
		}
	}

	PrepareThumbTextInfo();

	SAFE_RELEASE( pObject );

	if( !(m_flags & eAssetFlags_CachedFieldsInfo) )
	{
		SetFlag( IAssetDisplay::eAssetFlags_CachedFieldsInfo, true );
		GetOwnerDisplayDatabase()->OnMetaDataChange(this);
	}

	return true;
}

bool CAssetModelItem::UnCache()
{
	if( !( m_flags & eAssetFlags_Cached ) )
	{
		return false;
	}

	SetFlag( IAssetDisplay::eAssetFlags_Cached, false );
	SAFE_RELEASE( m_pObject );

	return true;
}

bool CAssetModelItem::UnCacheThumbnail()
{
	if( !( m_flags & eAssetFlags_ThumbnailCached ) )
	{
		return false;
	}

	SetFlag( eAssetFlags_ThumbnailCached, false );
	m_oCachedBmp.Free();

	return true;
}

void CAssetModelItem::OnBeginPreview( const HWND hQuickPreviewWnd, const HDC hMemDC )
{
}

void CAssetModelItem::OnEndPreview()
{
}

CDialog* CAssetModelItem::GetCustomPreviewPanel( CWnd* pParentWnd )
{
	s_modelPreviewDlg.m_pModel = this;

	if( !s_modelPreviewDlg.GetSafeHwnd() )
	{
		if( !s_modelPreviewDlg.Create( CAssetBrowserPreviewModelDlg::IDD, (CWnd*)pParentWnd ) )
		{
			AfxMessageBox( "Could not create model preview dialog panel" );
		}
	}
	else
	{
		s_modelPreviewDlg.SetParent( (CWnd*)pParentWnd );
		s_modelPreviewDlg.Init();
	}

	return &s_modelPreviewDlg;
}

void CAssetModelItem::InteractiveRender(	const HWND hRenderWindow, const CRect& rstViewport, int aMouseX, int aMouseY,
																					int aMouseDeltaX, int aMouseDeltaY, int aMouseWheelDelta, UINT aKeyFlags )
{
	// this way we can zoom and pan in the same time
	if( ( aKeyFlags & MK_SHIFT ) || ( aKeyFlags & MK_CONTROL ) )
	{
		if( aKeyFlags & MK_SHIFT )
		{
			m_camZoom += (float) aMouseDeltaX * AssetViewer::kInteractiveRenderModelZoomMultiplier;
		}

		if( aKeyFlags & MK_CONTROL )
		{
			m_translateX += (float) aMouseDeltaX * AssetViewer::kInteractiveRenderModelTranslateMultiplier;
			m_translateY += (float) aMouseDeltaY * AssetViewer::kInteractiveRenderModelTranslateMultiplier;
		}
	}
	else
	{
		m_rotationY += (float) aMouseDeltaX * AssetViewer::kInteractiveRenderModelRotateMultiplier;
		m_rotationX += (float) aMouseDeltaY * AssetViewer::kInteractiveRenderModelRotateMultiplier;
	}

	m_camZoom -= AssetViewer::kModelPreviewCameraZoomFactor * (float)aMouseWheelDelta / WHEEL_DELTA;

	Render( hRenderWindow, rstViewport, false );
}

void CAssetModelItem::OnInteractiveRenderKeyEvent( bool bKeyDown, UINT aChar, UINT aKeyFlags )
{
	if( bKeyDown && aChar == AssetViewer::kModelAssetWireframeToggleKey )
	{
		m_bWireframe = !m_bWireframe;
	}
}

bool CAssetModelItem::Render( const HWND hRenderWindow, const CRect& rstViewport, bool bCacheThumbnail )
{
	if( !( m_flags & eAssetFlags_Cached ) || !m_pObject )
	{
		return false;
	}

	if( m_pObject->m_eStreamingStatus != ecss_Ready )
		return false;

	gEnv->p3DEngine->Update();
	gEnv->pSystem->GetStreamEngine()->Update();

	m_pRenderer->SetCurrentContext( hRenderWindow );
	m_pRenderer->ChangeViewport( rstViewport.left, rstViewport.top, rstViewport.Width(), rstViewport.Height() );
	m_pRenderer->SetClearColor( Vec3( m_clearColor.r, m_clearColor.g, m_clearColor.b ) );
	m_pRenderer->ClearBuffer( FRT_CLEAR, &m_clearColor );
	m_pRenderer->BeginFrame();
	CalculateCameraPosition();
	SetCamera( m_camera, rstViewport);

	if( m_bGrid )
	{
		DrawGrid();
		m_pRenderer->GetIRenderAuxGeom()->Flush();
	}

	// Render object.
	bool bOldFog = m_pRenderer->EnableFog(false);

	XmlNodeRef oldTod = GetISystem()->CreateXmlNode();
	assert(oldTod);
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	assert(pTimeOfDay);
	float fTime = 0;
	
	if (pTimeOfDay)
	{
		fTime = pTimeOfDay->GetTime();
	
		if (AssetViewer::s_assetThumbTimeOfDay != NULL)
		{
			pTimeOfDay->Serialize(oldTod, false);
			pTimeOfDay->Serialize(AssetViewer::s_assetThumbTimeOfDay, true);
			pTimeOfDay->Update();
		}
	}

	m_pRenderer->EF_StartEf();
	m_pRenderer->ResetToDefault();

	m_pRenderer->SetWireframeMode( m_bWireframe ? R_WIREFRAME_MODE : R_SOLID_MODE );

	// Add lights.
	for( int i = 0, iCount = m_lights.size(); i < iCount; ++i )
	{
		m_pRenderer->EF_ADDDlight( &m_lights[i] );
	}

	SRendParams rp;
	rp.nDLightMask = 0x3;
	rp.AmbientColor = ColorF( 1, 1, 1, 1 );
	rp.dwFObjFlags |= FOB_TRANS_MASK;
	
	Matrix34 tm;
	tm.SetIdentity();
	rp.pMatrix = &tm;

	if( m_bShowObject && m_pObject )
	{
			m_pObject->Render( rp );
	}
	m_pRenderer->EF_EndEf3D( SHDF_NOASYNC | SHDF_STREAM_SYNC, -1, -1 );
	m_pRenderer->FlushTextMessages();
	m_pRenderer->RenderDebug(false);
	m_pRenderer->EndFrame();

	m_pRenderer->EnableFog(bOldFog);

	if (pTimeOfDay)
	{
		pTimeOfDay->Serialize(oldTod, true);
		pTimeOfDay->Update();
		pTimeOfDay->SetTime(fTime, true);
	}

	if( bCacheThumbnail )
	{
		m_pRenderer->ReadFrameBufferFast( m_frameBufferScreenshot, gSettings.sAssetBrowserSettings.nThumbSize, gSettings.sAssetBrowserSettings.nThumbSize );
		m_oCachedBmp.Create( m_frameBufferScreenshot, gSettings.sAssetBrowserSettings.nThumbSize, gSettings.sAssetBrowserSettings.nThumbSize, true, false );
		m_flags |= eAssetFlags_ThumbnailCached;
		
		// lets push this asset to the thumbs pool/queue, so we can have a pool of thumbs kept in memory until they are too old
		// this help browsing the assets, no need to recache each time a row of assets thumbs previously 
		if( m_piOwnerDatabase && m_piOwnerDatabase->GetAssociatedViewer() )
		{
			m_piOwnerDatabase->GetAssociatedViewer()->PushToThumbsCacheQueue( this );
		}
	}

	// Restore main context.
	m_pRenderer->MakeMainContextActive();

	return true;
}

void	CAssetModelItem::CalculateCameraPosition()
{
	AABB		box(m_aabb);
	Vec3		stCameraDirection(0.0f,0.0f,0.0f);
	float		fCameraDistance(0);
	float		afAABBDimensions[3];
	float		afRatios[3];
	float   fSmallestRatio(FLT_MAX);
	float		afCameraDistances[2];
	float		fSign(-1.0f);
	int32		nCount(0);
	int32		nSmallestRationIndex(0);
	int32		nCameraDimensionIndex(0);
	// If m_fov==45+180*K, FPE.
	float		fHalfFovTangent(tanf(DEG2RAD(m_fov/2.0f)));

	for (nCount=0;nCount<3;++nCount)
	{
		afAABBDimensions[nCount]=box.max[nCount]-box.min[nCount];
	}

	for (nCount=0;nCount<3;++nCount)
	{
		if (afAABBDimensions[(nCount+1)%3]!=0)
		{
			afRatios[nCount]=fabs(fabs(afAABBDimensions[nCount]/afAABBDimensions[(nCount+1)%3])-1.0f);
		}
		else
		{
			afRatios[nCount]=FLT_MAX;
		}
	}

	for (nCount=0;nCount<3;++nCount)
	{
		if (fSmallestRatio<afRatios[nCount])
		{
			fSmallestRatio=afRatios[nCount];
			nSmallestRationIndex=nCount;
		}
	}

	nSmallestRationIndex = 2;
	nCameraDimensionIndex = (nSmallestRationIndex + 2) % 3;
	
	if( ( ( nCameraDimensionIndex + 1 ) % 3 ) == 2 )
	{
		fSign *= fSign;
	}

	stCameraDirection[nCameraDimensionIndex] = 1.0f * fSign;

	// We want to look at the center of the front face of the AABB.
	m_camTarget = ( box.max + box.min ) * 0.5f;

	afCameraDistances[0] = m_camTarget[(nCameraDimensionIndex+1) % 3] - m_aabb.min[(nCameraDimensionIndex + 1)%3];
	afCameraDistances[1] = m_camTarget[(nCameraDimensionIndex+2) % 3] - m_aabb.min[(nCameraDimensionIndex + 2)%3];
	fCameraDistance = MAX( afCameraDistances[0], afCameraDistances[1] ) / fHalfFovTangent;
	fCameraDistance = box.GetRadius();
	stCameraDirection.Set( 0.0f, -1.0f, 0.0f );

	if( fCameraDistance < m_camera.GetNearPlane() )
	{
		fCameraDistance = m_camera.GetNearPlane();
	}

	// grow a little bit, further from camera center, add padding
	fCameraDistance *= 1.8f;
	Vec3 pos = stCameraDirection;
	Matrix33 rot;

	rot.SetIdentity();
	rot.SetRotationXYZ( Ang3( m_rotationX, 0, -m_rotationY ) );
	stCameraDirection = -rot.TransformVector( pos );
	Matrix34 tm = Matrix33::CreateRotationVDir( stCameraDirection, 0 );
	
	Vec3 vLeft = Vec3( -1, 0, 0 );
	Vec3 vUp = Vec3( 0, 0, 1 );
	
	vLeft = tm.TransformVector( vLeft );
	vUp = tm.TransformVector( vUp );

	Vec3 panVector = vUp*m_translateY + vLeft*m_translateX;
	m_camTarget += panVector;
	tm.SetTranslation( m_camTarget - stCameraDirection*fCameraDistance*m_camZoom + panVector );
	m_camera.SetMatrix( tm );
}

void* CAssetModelItem::CreateInstanceInViewport( float x, float y, float z )
{
	CBaseObject *pObj = GetIEditor()->GetObjectManager()->NewObject( "Brush", 0, CString( GetRelativePath() ) + GetFilename() );
	GetIEditor()->GetObjectManager()->BeginEditParams( pObj, OBJECT_CREATE );
	GetIEditor()->SetModifiedFlag();
	GetIEditor()->SetModifiedModule(eModifiedBrushes);
	pObj->SetPos( Vec3( x, y, z ) );
	GetIEditor()->GetObjectManager()->EndEditParams();

	return pObj;
}

bool CAssetModelItem::MoveInstanceInViewport( const void* pDraggedObject, float aNewX, float aNewY, float aNewZ )
{
	CBaseObject* pObj = (CBaseObject*)pDraggedObject;

	if( pObj )
	{
		pObj->SetPos( Vec3( aNewX, aNewY, aNewZ ) );
		return true;
	}

	return false;
}

void CAssetModelItem::AbortCreateInstanceInViewport( const void* pDraggedObject )
{
	CBaseObject* pObj = (CBaseObject*)pDraggedObject;

	if( pObj )
	{
		 GetIEditor()->GetObjectManager()->DeleteObject( pObj );
	}
}

void CAssetModelItem::DrawGrid()
{
	float step = 0.1f;
	float XR = 5;
	float YR = 5;

	IRenderAuxGeom * pRag = m_pRenderer->GetIRenderAuxGeom();
	SAuxGeomRenderFlags nRendFlags = pRag->GetRenderFlags();

	pRag->SetRenderFlags( e_Def3DPublicRenderflags );
	SAuxGeomRenderFlags nNewFlags = pRag->GetRenderFlags();
	nNewFlags.SetAlphaBlendMode( e_AlphaBlended );
	pRag->SetRenderFlags( nNewFlags );

	int nGridAlpha = 40;
	if (m_bGrid)
	{
		// Draw grid.
		for (float x = -XR; x < XR; x+=step)
		{
			if (fabs(x) > 0.01)
				//m_pRenderer->DrawLine( Vec3(x,-YR,0),Vec3(x,YR,0) );
				pRag->DrawLine( Vec3(x,-YR,0), ColorB(150, 150, 150 , nGridAlpha), Vec3(x,YR,0) , ColorB(150, 150, 150 , nGridAlpha) );
		}
		for (float y = -YR; y < YR; y+=step)
		{
			if (fabs(y) > 0.01)
				//m_pRenderer->DrawLine( Vec3(-XR,y,0),Vec3(XR,y,0) );
				pRag->DrawLine( Vec3(-XR,y,0), ColorB(150, 150, 150 , nGridAlpha), Vec3(XR,y,0) , ColorB(150, 150, 150 , nGridAlpha) );
		}
	}

	nGridAlpha = 60;
	if (m_bAxis)
	{
		// Draw axis.
		//m_pRenderer->SetMaterialColor( 1,0,0,1.0f );
		//m_pRenderer->DrawLine( Vec3(-XR,0,0),Vec3(XR,0,0) );
		pRag->DrawLine( Vec3(0,0,0), ColorB(255, 0, 0 ,nGridAlpha), Vec3(XR,0,0) , ColorB(255, 0, 0 ,nGridAlpha) );

		//m_pRenderer->SetMaterialColor( 0,1,0,1.0f );
		//m_pRenderer->DrawLine( Vec3(0,-YR,0),Vec3(0,YR,0) );
		pRag->DrawLine( Vec3(0,0,0), ColorB(0, 255, 0 ,nGridAlpha), Vec3(0,YR,0) , ColorB(0, 255, 0 ,nGridAlpha) );

		//m_pRenderer->SetMaterialColor( 0,0,1,1.0f );
		//m_pRenderer->DrawLine( Vec3(0,0,-YR),Vec3(0,0,YR) );
		pRag->DrawLine(Vec3(0,0,0), ColorB(0, 0, 255 ,nGridAlpha), Vec3(0,0,YR) , ColorB(0, 0, 255 ,nGridAlpha) );
	}
	pRag->SetRenderFlags( nRendFlags );
}

void CAssetModelItem::SetCamera( CCamera &cam,const CRect &rcViewportRect )
{
	int w = rcViewportRect.Width();
	int h = rcViewportRect.Height();

	m_camera.SetPosition( cam.GetPosition() );
	m_camera.SetFrustum( w, h, DEG2RAD( m_fov ), m_camera.GetNearPlane(), m_camera.GetFarPlane() );
	m_camera.SetViewPort( rcViewportRect.left, rcViewportRect.top, rcViewportRect.Width(), rcViewportRect.Height() );
	m_pRenderer->SetCamera( m_camera );
}

bool CAssetModelItem::GetAssetFieldValue( const char* pFieldName, void* pDest )
{
	if( IS_ASSET_FIELD_NAME( "thumbToolTipText" ) )
	{
		*(CString*)pDest = m_toolTipText.GetBuffer();
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "thumbOneLineText" ) )
	{
		*(CString*)pDest = m_toolTipOneLineText.GetBuffer();
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "islod" ) )
	{
		*(bool*)pDest = m_bIsLod;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "trianglecount" ) )
	{
		*(int*)pDest = m_triangleCount;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "vertexcount" ) )
	{
		*(int*)pDest = m_vertexCount;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "lodcount" ) )
	{
		*(int*)pDest = m_lodCount;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "submeshcount" ) )
	{
		*(int*)pDest = m_submeshCount;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "bbox_x" ) )
	{
		*(float*)pDest = m_aabb.GetSize().x;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "bbox_y" ) )
	{
		*(float*)pDest = m_aabb.GetSize().y;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "bbox_z" ) )
	{
		*(float*)pDest = m_aabb.GetSize().z;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "nref" ) )
	{
		*(int*)pDest = m_nRef;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "texturesize" ) )
	{
		*(int*)pDest = m_textureSize;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "phytricount" ) )
	{
		*(int*)pDest = m_physicsTriCount;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "physize" ) )
	{
		*(int*)pDest = m_physicsSize;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "splitlods" ) )
	{
		*(bool*)pDest = m_bSplitLODs;
		return true;
	}
	else
	if( IS_ASSET_FIELD_NAME( "lodstricount" ) )
	{
		CString lodsTris;
		MakeLODsTrisString( lodsTris );
		*(CString*)pDest = lodsTris;
		return true;
	}

	// else, check if the common fields are requested
	return CAssetDisplay::GetAssetFieldValue( pFieldName, pDest );
}

bool CAssetModelItem::DrawThumbImage( HDC hDC, const CRect& rRect )
{
	if( m_flags & eAssetFlags_ThumbnailCached )
	{
		CDC dc;

		dc.Attach( hDC );
		dc.BitBlt( rRect.left, rRect.top, rRect.Width(), rRect.Height(), &m_oCachedBmp.GetDC(), 0, 0, SRCCOPY );
		dc.Detach();

		return true;
	}

	return false;
}

void CAssetModelItem::CacheFieldsInfoForLoadedStatObj( IStatObj *pStatObj )
{
	if( m_flags & eAssetFlags_CachedFieldsInfo )
		return;

	IStatObj::SStatistics stats;
	stats.pTextureSizer = gEnv->pSystem->CreateSizer();

	pStatObj->GetStatistics( stats );

	if( !stats.nIndices || !stats.nVertices )
		return;

	m_aabb.min = pStatObj->GetBoxMin();
	m_aabb.max = pStatObj->GetBoxMax();

	m_lodCount = stats.nLods;
	m_triangleCount = stats.nIndices / 3;
	m_vertexCount = stats.nVertices;
	m_submeshCount = stats.nSubMeshCount;
	m_nRef = stats.nNumRefs;
	m_physicsTriCount = stats.nPhysPrimitives;
	m_physicsSize = (stats.nPhysProxySize + 512) / 1024;	// In KBs
	m_bSplitLODs = stats.bSplitLods;
	m_textureSize = stats.pTextureSizer->GetTotalSize() / 1024;		// In KBs
	for( int i = 0; i < MAX_STATOBJ_LODS_NUM; ++i )
		m_triCountLOD[i] = stats.nIndicesPerLod[i] / 3;
	stats.pTextureSizer->Release();

	size_t subMtls = pStatObj->GetMaterial()->GetSubMtlCount();
	IMaterial* pMtl = NULL;
	m_dependencies.clear();

	if( !subMtls )
	{
		pMtl = pStatObj->GetMaterial();

		// fill up dependencies
		if( pMtl )
		{
			IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

			m_dependencies["Materials"].insert( pMtl->GetName() );

			if( pShaderRes )
			{
				for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
				{
					m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
				}
			}
		}
	}
	else
	for( size_t s = 0; s < subMtls; ++s )
	{
		IMaterial* pMtl = pStatObj->GetMaterial()->GetSubMtl( s );

		// fill up dependencies
		if( pMtl )
		{
			IRenderShaderResources* pShaderRes = pMtl->GetShaderItem().m_pShaderResources;

			m_dependencies["Materials"].insert( pMtl->GetName() );

			if( pShaderRes )
			{
				for( size_t j = 0; SEfResTexture* pTex = pShaderRes->GetTexture( j ); ++j )
				{
					m_dependencies["Textures"].insert( pTex->m_Name.c_str() );
				}
			}
		}
	}

	CString tagsText, lodTris, lod;
	CAssetDisplay::GetAssetFieldValue( "tags", &tagsText );
	MakeLODsTrisString( lodTris );
	PrepareThumbTextInfo();
	SetFlag( IAssetDisplay::eAssetFlags_CachedFieldsInfo, true );
	GetOwnerDisplayDatabase()->OnMetaDataChange(this);
}

void CAssetModelItem::DrawTextOnReportImage( CAlphaBitmap &abm ) const
{
	const COLORREF filenameShadowColor = RGB( 0, 0, 0 );
	const COLORREF filenameColor = RGB( 255, 255, 0 );
	const COLORREF otherInfosColor = RGB( 0, 0, 0 );
	CFont fontInfoTitle, fontInfo;
	fontInfoTitle.CreatePointFont(95, "Arial Bold");
	fontInfo.CreatePointFont(80, "Arial");

	CDC dc;
	dc.Attach(abm.GetDC());
	dc.SetBkMode(TRANSPARENT);
	dc.SelectObject( fontInfoTitle );
	dc.SetTextColor(filenameShadowColor);
	dc.TextOut(AssetViewer::kOverlayTextLeftMargin+1, 
						AssetViewer::kOverlayTextTopMargin+1, m_strFilename);
	dc.SetTextColor(filenameColor);
	dc.TextOut(AssetViewer::kOverlayTextLeftMargin, 
						AssetViewer::kOverlayTextTopMargin, m_strFilename);

	CString lodsTris;
	
	MakeLODsTrisString( lodsTris );

	CString reportInfos;
	reportInfos.Format( 
		"Path: %s\nTrianges: %d\nVertices: %d\nLODs: %d\nLODs tris: %s\nSplit LODs: %s\nPhysics tris: %d\nRefs: %d\n",
		m_strRelativePath,
		m_triangleCount,
		m_vertexCount,
		m_lodCount,
		lodsTris.GetBuffer(),
		m_bSplitLODs ? "Yes" : "No",
		m_physicsTriCount,
		m_nRef
		);

	CSize	titleSize;
	titleSize = dc.GetTextExtent(m_strFilename);
	CRect rcTextInfo;
	rcTextInfo.left = AssetViewer::kOverlayTextLeftMargin;
	rcTextInfo.right = abm.GetWidth();
	rcTextInfo.top = titleSize.cy + 15;
	rcTextInfo.bottom = abm.GetHeight();
	dc.SetTextColor(otherInfosColor);
	dc.SelectObject(fontInfo);
	dc.DrawText(reportInfos.GetBuffer(), &rcTextInfo, DT_WORDBREAK);
	dc.Detach();

	fontInfoTitle.DeleteObject();
	fontInfo.DeleteObject();
}

bool CAssetModelItem::SaveReportImage( const char *filePath ) const
{
	if((m_flags & eAssetFlags_ThumbnailCached) == 0)
		return false;

	CAlphaBitmap& abm = const_cast<CAlphaBitmap&>(m_oCachedBmp);
	CBitmap& bitmap = abm.GetBitmap();
	BITMAP bmpInfo;
	bitmap.GetBitmap(&bmpInfo);
	int byteCount = bmpInfo.bmWidth*bmpInfo.bmWidth*(bmpInfo.bmBitsPixel/8);

	bool bImageIsWideEnough = bmpInfo.bmWidth >= AssetViewer::kMinWidthForOverlayText;

	std::vector<unsigned char> bitsBackUp;
	if(bImageIsWideEnough)					// Back up the bitmap and draw the overlay text.
	{
		bitsBackUp.resize(byteCount);
		bitmap.GetBitmapBits(byteCount, &bitsBackUp[0]);
		DrawTextOnReportImage(abm);
	}

	CImage image;
	image.Allocate(bmpInfo.bmWidth, bmpInfo.bmWidth);
	bitmap.GetBitmapBits(byteCount, image.GetData());
	image.SwapRedAndBlue();

	if(bImageIsWideEnough)				// Restore the bitmap.
		bitmap.SetBitmapBits(byteCount, &bitsBackUp[0]);

	return CImageUtil::SaveBitmap(filePath, image);
}

bool CAssetModelItem::SaveReportText( const char *filePath ) const
{
	FILE *file = fopen(filePath, "wt");
	
	if(file == NULL)
		return false;

	CString lodsTris;
	
	MakeLODsTrisString( lodsTris );

	fprintf(file, 
					"Filename: %s\nPath: %s\nTrianges: %d\nVertices: %d\nLODs: %d\nLODs tris: %s\nSplit LODs: %s\nPhysics tris: %d\nRefs: %d\n",
					m_strFilename,
					m_strRelativePath,
					m_triangleCount,
					m_vertexCount,
					m_lodCount,
					lodsTris.GetBuffer(),
					m_bSplitLODs ? "Yes" : "No",
					m_physicsTriCount,
					m_nRef);

	fclose(file);

	return true;
}

void CAssetModelItem::MakeLODsTrisString( CString& rOutValue ) const
{
	rOutValue = "";

	for( int lod = 0; lod < MAX_STATOBJ_LODS_NUM; ++lod )
	{
		if( m_triCountLOD[lod] <= 0 )
			break;

		CString count;
		
		count.Format("%d", m_triCountLOD[lod]);
		
		if( lod > 0 )
		{
			rOutValue += " / ";
		}

		rOutValue += count;
	}	
}

void CAssetModelItem::MakeLODsVertsString( CString& rOutValue ) const
{
	rOutValue = "";

	for( int lod = 0; lod < MAX_STATOBJ_LODS_NUM; ++lod )
	{
		if( m_vertCountLOD[lod] <= 0 )
			break;

		CString count;

		count.Format("%d", m_vertCountLOD[lod]);

		if( lod > 0 )
		{
			rOutValue += " / ";
		}

		rOutValue += count;
	}	
}

void CAssetModelItem::MakeLODsSubMeshesString( CString& rOutValue ) const
{
	rOutValue = "";

	for( int lod = 0; lod < MAX_STATOBJ_LODS_NUM; ++lod )
	{
		if( m_subMeshCountLOD[lod] <= 0 )
			break;

		CString count;

		count.Format("%d", m_subMeshCountLOD[lod]);

		if( lod > 0 )
		{
			rOutValue += " / ";
		}

		rOutValue += count;
	}	
}

void CAssetModelItem::MakeLODsMeshSizeString( CString& rOutValue ) const
{
	rOutValue = "";

	for( int lod = 0; lod < MAX_STATOBJ_LODS_NUM; ++lod )
	{
		if( m_meshSizeLOD[lod] <= 0 )
			break;

		CString count;

		count.Format("%0.1f", (float)m_meshSizeLOD[lod] / 1024);

		if( lod > 0 )
		{
			rOutValue += " / ";
		}

		rOutValue += count;
	}	
}

void CAssetModelItem::PrepareThumbTextInfo()
{
	CString tagsText, lodTris, lodVerts, lodSubs, lodMeshSize, lod;
	CAssetDisplay::GetAssetFieldValue( "tags", &tagsText );

	MakeLODsTrisString( lodTris );
	MakeLODsVertsString( lodVerts );
	MakeLODsSubMeshesString( lodSubs );
	MakeLODsMeshSizeString( lodMeshSize );

	m_toolTipText.Format(	"Path: %s\nLODs: %d\nTriangles/LOD: %s\nVertices/LOD: %s\nSubMeshes/LOD: %s\nMeshSize/LOD: %s (kB)\n"
												"BBox.X: %.4f\nBBox.Y: %.4f\nBBox.Z: %.4f\nFilesize: %.2f kB\nTextures: %d\nMaterials: %d\nTags: %s\n",
												m_strRelativePath.GetBuffer(),
												m_lodCount,
												lodTris.GetBuffer(),
												lodVerts.GetBuffer(),
												lodSubs.GetBuffer(),
												lodMeshSize.GetBuffer(),
												m_aabb.GetSize().x, m_aabb.GetSize().y, m_aabb.GetSize().z,
												(float) m_nFileSize / 1024.0f, m_dependencies["Textures"].size(),
												m_dependencies["Materials"].size(), tagsText.GetBuffer() );
	m_toolTipOneLineText.Format( "[ Tris/LOD: %s ]", lodTris.GetBuffer() );
}

void CAssetModelItem::CheckIfItsLod()
{
	m_bIsLod = (bool) strstr( m_strFilename.GetBuffer(), "_lod" );
}

void CAssetModelItem::ToXML(XmlNodeRef& node) const
{
	assert(m_flags & eAssetFlags_CachedFieldsInfo);

	node->setTag("Model");
	CString fileName = m_strRelativePath + m_strFilename;
	node->setAttr("fileName", fileName.GetBuffer());
	XmlNodeRef aabbNode = node->newChild("aabb");
	aabbNode->setAttr("min", m_aabb.min);
	aabbNode->setAttr("max", m_aabb.max);

	node->setAttr("lodCount", m_lodCount);
	node->setAttr("triangleCount", m_triangleCount);
	node->setAttr("vertexCount", m_vertexCount);
	node->setAttr("submeshCount", m_submeshCount);
	node->setAttr("nRef", m_nRef);
	node->setAttr("physicsTriCount", m_physicsTriCount);
	node->setAttr("physicsSize", m_physicsSize);
	node->setAttr("bSplitLODs", m_bSplitLODs);
	node->setAttr("textureSize", m_textureSize);
	bool bIsFav = m_flags & eAssetFlags_Favourite;
	node->setAttr("fav", bIsFav);

	for( int i = 0; i < MAX_STATOBJ_LODS_NUM; ++i )
	{
		XmlNodeRef triCountLODnode = node->newChild("triCountLOD");
		triCountLODnode->setAttr("lod", i);
		triCountLODnode->setAttr("triCount", m_triCountLOD[i]);
	}

	//TODO: save dependencies
	//for(size_t i=0; i<m_dependencies.size(); ++i)
	//{
	//	XmlNodeRef dependencyNode = node->newChild("dependency");
	//	dependencyNode->setAttr("path", m_dependencies[i].c_str());
	//}
}

void CAssetModelItem::FromXML(const XmlNodeRef& node)
{
	assert((m_flags & eAssetFlags_CachedFieldsInfo) == 0);
	assert(node->isTag("Model"));

	if(node->isTag("Model") == false)
		return;

	XmlNodeRef aabbNode = node->findChild("aabb");

	if( aabbNode == NULL )
	{
		assert(0);
		return;
	}

	aabbNode->getAttr("min", m_aabb.min);
	aabbNode->getAttr("max", m_aabb.max);
	node->getAttr("lodCount", m_lodCount);
	node->getAttr("triangleCount", m_triangleCount);
	node->getAttr("vertexCount", m_vertexCount);
	node->getAttr("submeshCount", m_submeshCount);
	node->getAttr("nRef", m_nRef);
	node->getAttr("physicsTriCount", m_physicsTriCount);
	node->getAttr("physicsSize", m_physicsSize);
	node->getAttr("bSplitLODs", m_bSplitLODs);
	node->getAttr("textureSize", m_textureSize);
	
	bool bIsFav = false;
	node->getAttr("fav", bIsFav);
	SetFlag( eAssetFlags_Favourite, bIsFav );
	m_dependencies.clear();
	
	for(int i=0; i<node->getChildCount(); ++i)
	{
		if(strcmp(node->getChild(i)->getTag(), "triCountLOD") == 0)
		{
			XmlNodeRef triCountLODnode = node->getChild(i);
			int index;
			triCountLODnode->getAttr("lod", index);
			triCountLODnode->getAttr("triCount", m_triCountLOD[index]);
		}
		//TODO: load dependencies
		//else if(strcmp(node->getChild(i)->getTag(), "dependency") == 0)
		//{
		//	XmlNodeRef dependencyNode = node->getChild(i);
		//	m_dependencies.push_back(dependencyNode->getAttr("path"));
		//}
	}

	SetFlag( IAssetDisplay::eAssetFlags_CachedFieldsInfo, true );
}
